package com.demo.controller;

import com.demo.Const.SDKConstants;
import com.demo.bean.DemoBase;
import com.demo.bean.UnionPayRequest;
import com.demo.bean.UnionRefundRequest;
import com.demo.util.AcpService;
import com.demo.util.Generator;
import com.demo.util.LogUtil;
import com.demo.util.SDKConfig;
import com.fasterxml.jackson.databind.util.BeanUtil;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.net.URLDecoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * @author xyd
 * @version V1.0
 * @Package com.demo.controller
 * @Description:
 * @date 2018/8/30 11:11
 */
@RestController
@RequestMapping("/demo")
public class DemoController {

    @GetMapping("/pay")
    public void pay(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        resp.setContentType("text/html; charset="+ DemoBase.encoding);

        UnionPayRequest unionPayRequest = new UnionPayRequest();
        unionPayRequest.setMerId("777290058162715");
        unionPayRequest.setOrderId(Generator.generateOrderCode());
        unionPayRequest.setTxnAmt("1");
        unionPayRequest.setTxnTime(new SimpleDateFormat("yyyyMMddHHmmss").format(new Date().getTime()));
        unionPayRequest.setPayTimeout(new SimpleDateFormat("yyyyMMddHHmmss").format(new Date().getTime() + 15 * 60 * 1000));

        Map<String, String> requestData = new HashMap<String, String>();
        try {
            requestData = BeanUtils.describe(unionPayRequest);
            requestData.remove("class");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        System.out.println(requestData);

        Map<String, String> submitFromData = AcpService.sign(requestData,DemoBase.encoding);

        String requestFrontUrl = SDKConfig.getConfig().getFrontRequestUrl();
        String html = AcpService.createAutoFormHtml(requestFrontUrl, submitFromData,DemoBase.encoding);
        LogUtil.writeLog("打印请求HTML，此为请求报文，为联调排查问题的依据："+html);
        resp.getWriter().write(html);
    }

    @PostMapping("/notify")
    public void notify(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        LogUtil.writeLog("BackRcvResponse接收后台通知开始");

        String encoding = req.getParameter(SDKConstants.param_encoding);
        // 获取银联通知服务器发送的后台通知参数
        Map<String, String> reqParam = getAllRequestParam(req);
        LogUtil.printRequestLog(reqParam);

        //重要！验证签名前不要修改reqParam中的键值对的内容，否则会验签不过
        if (!AcpService.validate(reqParam, encoding)) {
            LogUtil.writeLog("验证签名结果[失败].");
            //验签失败，需解决验签问题

        } else {
            LogUtil.writeLog("验证签名结果[成功].");
            //【注：为了安全验签成功才应该写商户的成功处理逻辑】交易成功，更新商户订单状态

            String orderId =reqParam.get("orderId"); //获取后台通知的数据，其他字段也可用类似方式获取
            String respCode = reqParam.get("respCode");
            //判断respCode=00、A6后，对涉及资金类的交易，请再发起查询接口查询，确定交易成功后更新数据库。

        }
        System.out.println("8*******8" + reqParam);
        LogUtil.writeLog("BackRcvResponse接收后台通知结束");
        //返回给银联服务器http 200  状态码
        resp.getWriter().print("ok");
    }

    @PostMapping("/refund")
    public void refund(HttpServletResponse resp, HttpServletRequest req) throws IOException {
        String origQryId = req.getParameter("origQryId");
        UnionRefundRequest unionRefundRequest = new UnionRefundRequest();
        unionRefundRequest.setMerId("777290058162715");
        unionRefundRequest.setTxnAmt("1");
        unionRefundRequest.setOrigQryId(origQryId);
        unionRefundRequest.setOrderId(Generator.generateOrderCode());
        unionRefundRequest.setTxnTime(DemoBase.getCurrentTime());

        Map<String, String> data = new HashMap<String, String>();
        try {
            data = BeanUtils.describe(unionRefundRequest);
            data.remove("class");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        /**请求参数设置完毕，以下对请求参数进行签名并发送http post请求，接收同步应答报文------------->**/
        Map<String, String> reqData  = AcpService.sign(data,DemoBase.encoding);//报文中certId,signature的值是在signData方法中获取并自动赋值的，只要证书配置正确即可。
        String url = SDKConfig.getConfig().getBackRequestUrl();//交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.backTransUrl

        Map<String, String> rspData = AcpService.post(reqData,url,DemoBase.encoding);//这里调用signData之后，调用submitUrl之前不能对submitFromData中的键值对做任何修改，如果修改会导致验签不通过

        /**对应答码的处理，请根据您的业务逻辑来编写程序,以下应答码处理逻辑仅供参考------------->**/
        //应答码规范参考open.unionpay.com帮助中心 下载  产品接口规范  《平台接入接口规范-第5部分-附录》
        if(!rspData.isEmpty()){
            if(AcpService.validate(rspData, DemoBase.encoding)){
                LogUtil.writeLog("验证签名成功");
                String respCode = rspData.get("respCode");
                if("00".equals(respCode)){
                    //交易已受理，等待接收后台通知更新订单状态,也可以主动发起 查询交易确定交易状态。
                    //TODO
                }else if("03".equals(respCode)||
                        "04".equals(respCode)||
                        "05".equals(respCode)){
                    //后续需发起交易状态查询交易确定交易状态
                    //TODO
                }else{
                    //其他应答码为失败请排查原因
                    //TODO
                }
            }else{
                LogUtil.writeErrorLog("验证签名失败");
                //TODO 检查验证签名失败的原因
            }
        }else{
            //未返回正确的http状态
            LogUtil.writeErrorLog("未获取到返回报文或返回http状态码非200");
        }

        String reqMessage = reqData.toString();
        String rspMessage = DemoBase.genHtmlResult(rspData);
        resp.getWriter().write("<br>请求报文:</br>"+reqMessage+"<br>" + "应答报文:</br>"+rspMessage+"");
    }

    /**
     * 获取请求参数中所有的信息
     * 当商户上送frontUrl或backUrl地址中带有参数信息的时候，
     * 这种方式会将url地址中的参数读到map中，会导多出来这些信息从而致验签失败，这个时候可以自行修改过滤掉url中的参数或者使用getAllRequestParamStream方法。
     * @param request
     * @return
     */
    public static Map<String, String> getAllRequestParam(
            final HttpServletRequest request) {
        Map<String, String> res = new HashMap<String, String>();
        Enumeration<?> temp = request.getParameterNames();
        if (null != temp) {
            while (temp.hasMoreElements()) {
                String en = (String) temp.nextElement();
                String value = request.getParameter(en);
                res.put(en, value);
                // 在报文上送时，如果字段的值为空，则不上送<下面的处理为在获取所有参数数据时，判断若值为空，则删除这个字段>
                if (res.get(en) == null || "".equals(res.get(en))) {
                    // System.out.println("======为空的字段名===="+en);
                    res.remove(en);
                }
            }
        }
        return res;
    }


}
